// ==UserScript== // @name 喵课助手 | 网课小工具 // @namespace http://nb.zizizi.top/ // @version 1.0 // @description 支持【超星学习通】【智慧树】【职教云系列】【雨课堂】【考试星】【168网校】【u校园】【大学MOOC】【云班课】【优慕课】【继续教育类】【绎通云课堂】【九江系列】【柠檬文才】【亿学宝云】【优课学堂】【小鹅通】【安徽继续教育】【上海开放大学】【华侨大学自考网络助学平台】【良师在线】【和学在线】【人卫慕课】【国家开放大学】【山财培训网】【浙江省高等学校在线开放课程共享平台】【国地质大学远程与继续教育学院】【重庆大学网络教育学院】【浙江省高等教育自学考试网络助学平台】【湖南高等学历继续教育】【优学院】【学起系列】【青书学堂】【学堂在线】【英华学堂】【广开网络教学平台】等,内置题库功能。Q群:1033538224 // @author 喵课团队 // @match *://*.edu.cn/* // @match *://*.chaoxing.com/* // @match *://*.zhihuishu.com/* // @match *://*.icve.com.cn/* // @match *://*.cnmooc.org/* // @match *://*.xuetangx.com/* // @match *://*.icourse163.org/* // @match *://*.yuketang.cn/* // @match *://*.mooc.cn/* // @match *://study.163.com/* // @match *://www.bilibili.com/video/* // @match *://v.qq.com/* // @icon http://nb.zizizi.top/miaoke.ico // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @license MIT // ==/UserScript== (function() { 'use strict'; // 工具类 class MiaoKeHelper { constructor() { this.version = '1.0'; this.siteName = this.detectSite(); this.playAttempts = {}; // 记录播放尝试次数 this.lastPlayTime = 0; // 最后一次尝试播放的时间 this.isUserPaused = false; // 用户是否主动暂停 this.init(); } // 检测当前网站 detectSite() { const host = window.location.hostname; if (host.includes('chaoxing.com')) return '超星学习通'; if (host.includes('zhihuishu.com')) return '智慧树'; if (host.includes('icve.com.cn')) return '智慧职教'; if (host.includes('xuetangx.com')) return '学堂在线'; if (host.includes('icourse163.org')) return '中国大学MOOC'; if (host.includes('bilibili.com')) return 'B站视频'; if (host.includes('v.qq.com')) return '腾讯视频'; return '教育平台'; } // 初始化 init() { this.addStyles(); this.createUI(); this.initFeatures(); this.bindEvents(); console.log(`喵课助手已启动 - ${this.siteName}`); } // 添加样式 addStyles() { GM_addStyle(` /* 主容器样式 */ #miaoke-helper-btn { position: fixed; z-index: 9999; right: 20px; top: 100px; width: 60px; height: 60px; border-radius: 50%; background: linear-gradient(135deg, #6a5af9, #d66efd); color: white; text-align: center; line-height: 60px; font-size: 28px; cursor: pointer; box-shadow: 0 4px 20px rgba(106, 90, 249, 0.4); transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); user-select: none; border: 2px solid rgba(255, 255, 255, 0.3); backdrop-filter: blur(5px); } #miaoke-helper-btn:hover { transform: scale(1.1) rotate(5deg); box-shadow: 0 6px 25px rgba(106, 90, 249, 0.6); } #miaoke-helper-panel { position: fixed; z-index: 9998; right: 20px; top: 100px; width: 340px; background: rgba(255, 255, 255, 0.95); border-radius: 16px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15); font-family: 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif; transition: all 0.4s cubic-bezier(0.165, 0.84, 0.44, 1); display: none; overflow: hidden; backdrop-filter: blur(10px); border: 1px solid rgba(106, 90, 249, 0.2); transform-origin: top right; } #miaoke-helper-panel.active { display: block; animation: panelFadeIn 0.4s forwards; } @keyframes panelFadeIn { from { opacity: 0; transform: translateY(-20px) scale(0.95); } to { opacity: 1; transform: translateY(0) scale(1); } } /* 面板头部 */ .helper-header { padding: 18px 20px; background: linear-gradient(135deg, #6a5af9, #d66efd); color: white; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid rgba(255, 255, 255, 0.1); } .helper-title { margin: 0; font-size: 18px; font-weight: 600; letter-spacing: 0.5px; display: flex; align-items: center; gap: 8px; } .helper-title:before { content: '🐱'; display: inline-block; font-size: 22px; } .helper-close { cursor: pointer; font-size: 22px; width: 30px; height: 30px; display: flex; align-items: center; justify-content: center; border-radius: 50%; transition: all 0.2s; background: rgba(255, 255, 255, 0.2); } .helper-close:hover { background: rgba(255, 255, 255, 0.3); transform: rotate(90deg); } /* 功能区 */ .helper-content { padding: 20px; max-height: 450px; overflow-y: auto; scrollbar-width: thin; } .helper-content::-webkit-scrollbar { width: 6px; } .helper-content::-webkit-scrollbar-track { background: rgba(0, 0, 0, 0.05); border-radius: 3px; } .helper-content::-webkit-scrollbar-thumb { background: rgba(106, 90, 249, 0.3); border-radius: 3px; } .helper-section { margin-bottom: 24px; padding-bottom: 8px; position: relative; } .section-title { font-size: 16px; font-weight: 600; margin-bottom: 14px; color: #333; border-bottom: 2px solid #eee; padding-bottom: 8px; position: relative; } .section-title:after { content: ''; position: absolute; bottom: -2px; left: 0; width: 50px; height: 2px; background: linear-gradient(90deg, #6a5af9, #d66efd); } .feature-container { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 10px; } .feature-btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 10px 16px; background: #f5f5f5; border-radius: 8px; cursor: pointer; font-size: 14px; color: #444; transition: all 0.3s; border: 1px solid #e0e0e0; min-width: 90px; } .feature-btn:before { font-size: 16px; } #auto-play:before { content: '▶️'; } #reading-mode:before { content: '📖'; } #take-notes:before { content: '📝'; } #speed-control:before { content: '⏱️'; } #auto-next:before { content: '⏭️'; } .feature-btn:hover { background: #EEEAFF; border-color: #d1caff; transform: translateY(-2px); box-shadow: 0 4px 12px rgba(106, 90, 249, 0.1); } .feature-btn.active { background: linear-gradient(135deg, #6a5af9, #d66efd); color: white; border-color: transparent; box-shadow: 0 4px 15px rgba(106, 90, 249, 0.3); } /* 设置区域 */ .helper-setting { margin-bottom: 14px; display: flex; justify-content: space-between; align-items: center; padding: 10px 15px; background: #f8f8f8; border-radius: 10px; transition: all 0.3s; border: 1px solid #eee; } .helper-setting:hover { background: #f0f0f0; border-color: #ddd; } .setting-label { font-size: 14px; color: #444; font-weight: 500; } .setting-input { width: 70px; text-align: center; border: 1px solid #ddd; border-radius: 6px; padding: 6px 8px; font-size: 14px; transition: all 0.3s; background: white; } .setting-input:focus { outline: none; border-color: #6a5af9; box-shadow: 0 0 0 3px rgba(106, 90, 249, 0.1); } /* 底部 */ .helper-footer { padding: 12px 15px; background: #f5f5f5; text-align: center; font-size: 12px; color: #666; border-top: 1px solid #eee; } .helper-footer a { color: #6a5af9; text-decoration: none; font-weight: 500; transition: all 0.2s; } .helper-footer a:hover { color: #d66efd; text-decoration: underline; } /* 笔记面板 */ #note-panel { position: fixed; right: 20px; bottom: 20px; width: 340px; height: 300px; background: white; border-radius: 16px; box-shadow: 0 8px 25px rgba(0,0,0,0.15); z-index: 9997; display: none; overflow: hidden; border: 1px solid rgba(106, 90, 249, 0.2); transition: all 0.4s cubic-bezier(0.165, 0.84, 0.44, 1); } #note-panel.active { display: block; animation: notePanelFadeIn 0.4s forwards; } @keyframes notePanelFadeIn { from { opacity: 0; transform: translateY(20px) scale(0.95); } to { opacity: 1; transform: translateY(0) scale(1); } } .note-header { padding: 15px; background: linear-gradient(135deg, #6a5af9, #d66efd); color: white; display: flex; justify-content: space-between; align-items: center; font-size: 16px; font-weight: 600; } .note-content { padding: 15px; height: calc(100% - 110px); } .note-textarea { width: 100%; height: 100%; border: 1px solid #e0e0e0; border-radius: 8px; padding: 10px; resize: none; font-size: 14px; line-height: 1.5; transition: all 0.3s; } .note-textarea:focus { outline: none; border-color: #6a5af9; box-shadow: 0 0 0 3px rgba(106, 90, 249, 0.1); } .note-footer { padding: 10px 15px; display: flex; justify-content: flex-end; background: #f5f5f5; } .note-save { padding: 8px 16px; background: linear-gradient(135deg, #6a5af9, #d66efd); color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; font-weight: 500; transition: all 0.3s; box-shadow: 0 4px 10px rgba(106, 90, 249, 0.2); } .note-save:hover { transform: translateY(-2px); box-shadow: 0 6px 15px rgba(106, 90, 249, 0.3); } /* 阅读模式 */ .reading-mode-active { background-color: #f8f9fa !important; color: #333 !important; font-size: 18px !important; line-height: 1.7 !important; letter-spacing: 0.3px !important; } .reading-mode-active p, .reading-mode-active div { max-width: 900px !important; margin: 0 auto !important; padding: 15px 30px !important; } /* 拖动功能 */ .draggable { cursor: move; } /* 状态提示 */ .status-tip { position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); background: rgba(0, 0, 0, 0.7); color: white; padding: 10px 20px; border-radius: 8px; z-index: 10000; font-size: 14px; opacity: 0; transition: opacity 0.3s; pointer-events: none; } .status-tip.show { opacity: 1; } /* 按钮动效 */ @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.05); } 100% { transform: scale(1); } } .feature-btn.active:before { animation: pulse 2s infinite; } /* 进度条 */ .progress-container { width: 100%; height: 6px; background: #f0f0f0; border-radius: 3px; overflow: hidden; margin-top: 5px; } .progress-bar { height: 100%; background: linear-gradient(90deg, #6a5af9, #d66efd); width: 0; transition: width 0.3s; } `); } // 创建用户界面 createUI() { // 主按钮 const btn = document.createElement('div'); btn.id = 'miaoke-helper-btn'; btn.innerHTML = '🐱'; btn.title = '喵课助手'; document.body.appendChild(btn); // 主面板 const panel = document.createElement('div'); panel.id = 'miaoke-helper-panel'; panel.innerHTML = `

喵课助手 - ${this.siteName}

×
学习辅助功能
自动播放
阅读模式
笔记工具
速度调节
自动下一章
视频状态
当前状态: 未检测到视频
自动播放: 未启用
喵课资源推荐
邀请码: 0000 必填
网课自动化解决方案: 访问
更多学习工具: 查看
`; document.body.appendChild(panel); // 笔记面板 const notePanel = document.createElement('div'); notePanel.id = 'note-panel'; notePanel.innerHTML = `
学习笔记 ×
`; document.body.appendChild(notePanel); // 状态提示 const statusTip = document.createElement('div'); statusTip.className = 'status-tip'; statusTip.id = 'status-tip'; document.body.appendChild(statusTip); } // 显示状态提示 showStatusTip(message, duration = 2000) { const tip = document.getElementById('status-tip'); tip.textContent = message; tip.classList.add('show'); setTimeout(() => { tip.classList.remove('show'); }, duration); } // 初始化功能 initFeatures() { // 获取保存的笔记 const savedNote = GM_getValue('miaoke_helper_note_' + window.location.href, ''); if (savedNote) { document.querySelector('.note-textarea').value = savedNote; } // 获取保存的设置 const savedSpeed = GM_getValue('miaoke_helper_speed', 1.5); document.getElementById('play-speed').value = savedSpeed; // 获取自动播放设置 const autoPlayEnabled = GM_getValue('miaoke_helper_autoplay', true); if (autoPlayEnabled) { document.getElementById('auto-play').classList.add('active'); this.enableAutoPlay(); } } // 绑定事件 bindEvents() { const self = this; // 主按钮点击 document.getElementById('miaoke-helper-btn').addEventListener('click', function() { const panel = document.getElementById('miaoke-helper-panel'); panel.classList.toggle('active'); }); // 关闭按钮 document.querySelectorAll('.helper-close').forEach(function(el) { el.addEventListener('click', function() { this.closest('#miaoke-helper-panel, #note-panel').classList.remove('active'); }); }); // 自动播放 document.getElementById('auto-play').addEventListener('click', function() { this.classList.toggle('active'); if (this.classList.contains('active')) { self.enableAutoPlay(); GM_setValue('miaoke_helper_autoplay', true); self.showStatusTip('已开启自动播放功能', 1500); } else { self.disableAutoPlay(); GM_setValue('miaoke_helper_autoplay', false); self.showStatusTip('已关闭自动播放功能', 1500); } }); // 阅读模式 document.getElementById('reading-mode').addEventListener('click', function() { this.classList.toggle('active'); document.body.classList.toggle('reading-mode-active'); }); // 笔记工具 document.getElementById('take-notes').addEventListener('click', function() { document.getElementById('note-panel').classList.toggle('active'); }); // 保存笔记 document.querySelector('.note-save').addEventListener('click', function() { const noteContent = document.querySelector('.note-textarea').value; GM_setValue('miaoke_helper_note_' + window.location.href, noteContent); self.showStatusTip('笔记已保存!', 1500); }); // 速度调节 document.getElementById('speed-control').addEventListener('click', function() { this.classList.toggle('active'); const speedSettings = document.getElementById('speed-settings'); speedSettings.style.display = speedSettings.style.display === 'none' ? 'block' : 'none'; }); // 应用速度 document.getElementById('apply-speed').addEventListener('click', function() { const speedValue = parseFloat(document.getElementById('play-speed').value); GM_setValue('miaoke_helper_speed', speedValue); self.applyVideoSpeed(speedValue); self.showStatusTip(`已将视频速度设为 ${speedValue}x`, 1500); }); // 自动下一章 document.getElementById('auto-next').addEventListener('click', function() { this.classList.toggle('active'); if (this.classList.contains('active')) { self.enableAutoNext(); self.showStatusTip('已开启自动下一章功能', 1500); } else { self.disableAutoNext(); self.showStatusTip('已关闭自动下一章功能', 1500); } }); // 拖动功能 this.enableDrag(document.querySelectorAll('.draggable')); } // 启用自动播放 enableAutoPlay() { // 立即尝试播放当前视频 this.autoPlayVideos(); // 更新自动播放状态 document.getElementById('autoplay-status').textContent = '已启用'; document.getElementById('autoplay-status').style.color = '#6a5af9'; // 监听 DOM 变化以捕捉新加载的视频 if (!this.mutationObserver) { this.mutationObserver = new MutationObserver((mutations) => { // 使用防抖,避免频繁调用 if (Date.now() - this.lastPlayTime > 1000) { this.autoPlayVideos(); this.lastPlayTime = Date.now(); } }); this.mutationObserver.observe(document.body, { childList: true, subtree: true }); } // 定时检查是否有需要播放的视频,降低检查频率 if (!this.autoPlayInterval) { this.autoPlayInterval = setInterval(() => { this.updateVideoStatus(); // 只在需要时才处理自动播放和弹窗 if (this.shouldTryAutoplay()) { this.autoPlayVideos(); this.handlePopupDialogs(); } }, 2000); // 增加间隔到2秒,减少干扰 } } // 判断是否应该尝试自动播放 shouldTryAutoplay() { // 获取所有视频元素 const videos = document.querySelectorAll('video'); if (videos.length === 0) return false; // 如果用户主动暂停,则不应该自动播放 if (this.isUserPaused) return false; // 检查是否有任何视频需要播放 let needsPlayback = false; videos.forEach(video => { // 只有当视频暂停且不是已结束状态时才需要播放 if (video.paused && !video.ended) { // 检查是否正在缓冲 if (video.readyState < 3) { // 正在缓冲,等待加载更多数据 return; } // 检查尝试次数,避免无限尝试 const videoId = video.src || video.currentSrc || video.id || 'unknown'; if (!this.playAttempts[videoId]) { this.playAttempts[videoId] = 0; } // 如果尝试次数过多,间隔至少30秒再试 if (this.playAttempts[videoId] > 5) { const lastAttempt = video.getAttribute('last-attempt-time') || 0; if (Date.now() - lastAttempt < 30000) { return; } // 重置尝试次数 this.playAttempts[videoId] = 0; } needsPlayback = true; } }); return needsPlayback; } // 实际处理自动播放的函数 autoPlayVideos() { // 获取所有视频元素 const videos = document.querySelectorAll('video'); if (videos.length > 0) { // 更新视频状态 document.getElementById('video-status').textContent = '已检测到视频'; document.getElementById('video-status').style.color = '#6a5af9'; // 播放每个视频 videos.forEach(video => { // 为视频添加事件监听器(如果尚未添加) if (!video.hasAttribute('miaoke-processed')) { // 添加播放错误处理 video.addEventListener('error', () => { console.log('视频播放出错'); this.showStatusTip('视频播放出错,尝试恢复...', 2000); setTimeout(() => this.clickPlayButton(), 500); }); // 添加进度更新 video.addEventListener('timeupdate', () => { this.updateVideoProgress(video); }); // 添加视频结束处理 video.addEventListener('ended', () => { if (document.getElementById('auto-next').classList.contains('active')) { setTimeout(() => this.findAndClickNextButton(), 1000); } }); // 添加用户交互检测 video.addEventListener('pause', () => { // 检测如果是用户手动暂停的 if (document.activeElement === video || Date.now() - this.lastPlayTime > 1000) { this.isUserPaused = true; console.log('检测到用户手动暂停'); // 五秒后重置用户暂停状态,允许系统再次尝试自动播放 setTimeout(() => { this.isUserPaused = false; }, 5000); } }); // 添加播放事件处理 video.addEventListener('play', () => { // 重置播放尝试次数 const videoId = video.src || video.currentSrc || video.id || 'unknown'; this.playAttempts[videoId] = 0; // 更新播放状态 this.isUserPaused = false; }); video.setAttribute('miaoke-processed', 'true'); } // 如果视频暂停且不是用户主动暂停 if (video.paused && !video.ended && !this.isUserPaused) { // 获取视频ID const videoId = video.src || video.currentSrc || video.id || 'unknown'; // 记录尝试时间 video.setAttribute('last-attempt-time', Date.now()); this.lastPlayTime = Date.now(); // 增加尝试次数 if (!this.playAttempts[videoId]) { this.playAttempts[videoId] = 0; } this.playAttempts[videoId]++; // 检查视频是否已缓冲足够数据 if (video.readyState >= 3) { // 先尝试点击播放按钮 const buttonClicked = this.clickPlayButton(); // 如果没有点击成功或者是第一次尝试,直接调用play() if (!buttonClicked || this.playAttempts[videoId] <= 1) { // 尝试直接播放视频元素 const playPromise = video.play(); // 处理可能的错误 if (playPromise !== undefined) { playPromise.catch(error => { console.log('自动播放失败,尝试备用方法:', error.message); // 备用方法:模拟用户交互后再尝试播放 if (this.playAttempts[videoId] < 3) { setTimeout(() => { // 短暂聚焦视频元素 video.focus(); // 模拟点击视频 video.click(); // 再次尝试播放 video.play().catch(e => { console.log('第二次播放尝试也失败:', e.message); // 最后的方法:尝试特定站点的解决方案 if (this.playAttempts[videoId] < 5) { this.trySiteSpecificAutoplay(); } }); }, 500); } }); } } } else { console.log('视频未完全加载,等待缓冲...'); } } // 应用当前设置的速度,但避免频繁设置 const currentTime = Date.now(); if (!video.lastSpeedApplied || currentTime - video.lastSpeedApplied > 5000) { const currentSpeed = parseFloat(document.getElementById('play-speed').value); if (video.playbackRate !== currentSpeed) { video.playbackRate = currentSpeed; video.lastSpeedApplied = currentTime; } } }); } else { // 没有视频的情况 document.getElementById('video-status').textContent = '未检测到视频'; document.getElementById('video-status').style.color = '#888'; // 重置进度条 const progressBar = document.getElementById('video-progress'); progressBar.style.width = '0%'; } } // 尝试特定网站的自动播放解决方案 trySiteSpecificAutoplay() { let success = false; switch(this.siteName) { case '超星学习通': // 超星特定方法 success = this.chaoxingSiteSpecific(); break; case '智慧树': // 智慧树特定方法 success = this.zhihuishuSiteSpecific(); break; case 'B站': // B站特定方法 success = this.bilibiliSiteSpecific(); break; default: // 通用方法,模拟空格键按下 try { document.dispatchEvent(new KeyboardEvent('keydown', { key: ' ', keyCode: 32, which: 32 })); document.dispatchEvent(new KeyboardEvent('keyup', { key: ' ', keyCode: 32, which: 32 })); console.log('已尝试模拟空格键播放'); success = true; } catch (e) { console.error('模拟空格键失败:', e); } break; } return success; } // 超星学习通特定解决方案 chaoxingSiteSpecific() { try { // 尝试查找并移除覆盖层 const overlays = document.querySelectorAll('.vjs-overlay, .ans-videoquiz'); overlays.forEach(overlay => overlay.remove()); // 尝试直接调用播放器的play方法 const playerObj = document.querySelector('#video_html5_api') || document.querySelector('.anv-video video'); if (playerObj && typeof playerObj.play === 'function') { playerObj.play(); console.log('超星学习通: 直接调用播放器方法'); return true; } // 尝试点击特定的播放按钮 const specificButton = document.querySelector('.vjs-play-control') || document.querySelector('.vjs-big-play-button'); if (specificButton) { specificButton.click(); console.log('超星学习通: 点击特定播放按钮'); return true; } return false; } catch (e) { console.error('超星学习通特定方法失败:', e); return false; } } // 智慧树特定解决方案 zhihuishuSiteSpecific() { try { // 尝试使用智慧树的视频对象 const videoContainer = document.querySelector('#mediaplayer'); if (videoContainer) { const playButton = videoContainer.querySelector('[class*="play-button"]') || videoContainer.querySelector('.controlsBar__play'); if (playButton) { playButton.click(); console.log('智慧树: 点击播放按钮'); return true; } } // 尝试移除对话框 const dialog = document.querySelector('.dialog-content'); if (dialog) { const okButton = dialog.querySelector('.ant-btn-primary'); if (okButton) { okButton.click(); console.log('智慧树: 关闭对话框'); return true; } } return false; } catch (e) { console.error('智慧树特定方法失败:', e); return false; } } // B站特定解决方案 bilibiliSiteSpecific() { try { // 尝试点击B站特定的播放按钮 const bButton = document.querySelector('.bpx-player-ctrl-play') || document.querySelector('.bilibili-player-video-btn-start'); if (bButton) { bButton.click(); console.log('B站: 点击播放按钮'); return true; } // 尝试使用B站的视频对象 const bVideo = document.querySelector('video.bilibili-player-video'); if (bVideo && typeof bVideo.play === 'function') { bVideo.play(); console.log('B站: 直接调用视频对象播放'); return true; } return false; } catch (e) { console.error('B站特定方法失败:', e); return false; } } // 查找并点击播放按钮 clickPlayButton() { // 基于网站类型查找播放按钮 let playButton = null; let clickSuccess = false; switch(this.siteName) { case '超星学习通': // 超星的播放按钮 playButton = document.querySelector('.vjs-play-control') || document.querySelector('.vjs-big-play-button') || document.querySelector('.playButton'); break; case '智慧树': // 智慧树的播放按钮 playButton = document.querySelector('.controlsBar__play') || document.querySelector('.controlsBar__bigPlay') || document.querySelector('[class*="play-button"]'); break; case 'B站': // B站的播放按钮 playButton = document.querySelector('.bpx-player-ctrl-play') || document.querySelector('.bilibili-player-video-btn-start'); break; default: // 通用播放按钮选择器 playButton = document.querySelector('button[title="播放"]') || document.querySelector('.vjs-play-control') || document.querySelector('[class*="play-button"]') || document.querySelector('[class*="play_button"]') || document.querySelector('[aria-label*="播放"]') || document.querySelector('[aria-label*="Play"]'); break; } // 如果找到了播放按钮且当前是暂停状态,则点击 if (playButton && (playButton.classList.contains('vjs-paused') || !playButton.classList.contains('vjs-playing'))) { try { // 首先尝试模拟鼠标事件 ['mouseover', 'mousedown', 'mouseup', 'click'].forEach(eventType => { const event = new MouseEvent(eventType, { bubbles: true, cancelable: true, view: window }); playButton.dispatchEvent(event); }); // 日志和状态记录 console.log('成功点击播放按钮'); clickSuccess = true; } catch (e) { console.error('点击播放按钮失败:', e); } } // 检查iframe内的视频播放按钮 if (!clickSuccess) { const iframes = document.querySelectorAll('iframe'); iframes.forEach(iframe => { try { if (iframe.contentDocument) { const iframePlayButton = iframe.contentDocument.querySelector('[class*="play-button"]') || iframe.contentDocument.querySelector('.vjs-play-control') || iframe.contentDocument.querySelector('[aria-label*="播放"]'); if (iframePlayButton) { try { ['mouseover', 'mousedown', 'mouseup', 'click'].forEach(eventType => { const event = new MouseEvent(eventType, { bubbles: true, cancelable: true, view: iframe.contentWindow }); iframePlayButton.dispatchEvent(event); }); console.log('成功点击iframe内的播放按钮'); clickSuccess = true; } catch (e) { console.error('点击iframe内播放按钮失败:', e); } } } } catch (e) { // 跨域iframe访问错误,正常情况 console.log('无法访问iframe内容 (可能是跨域限制)'); } }); } return clickSuccess; } // 处理弹窗和对话框 handlePopupDialogs() { // 处理常见的弹窗 const dialogSelectors = [ '.video-answer', // 超星的视频浏览会有的弹题 '.question-wrapper', // 智慧树的题目 '.dialog-test', // 一般测试弹窗 '.popbox', // 常见弹窗 '[class*="dialog"]', // 包含 dialog 的类 '[class*="popup"]', // 包含 popup 的类 '[class*="alert"]', // 包含 alert 的类 '[class*="modal"]', // 包含 modal 的类 // 特定平台的选择器 '.ans-videoquiz', // 超星弹题 '.ans-videoquiz-opt', // 超星选项 '.topic-item' // 智慧树题目 ]; // 尝试处理匹配到的弹窗 let dialogHandled = false; for (let selector of dialogSelectors) { const dialogs = document.querySelectorAll(selector); for (let dialog of dialogs) { if (dialog && (dialog.style.display !== 'none' && dialog.offsetParent !== null)) { // 尝试自动回答问题(对超星和智慧树常见的选择题) if (this.tryToAnswerQuestion(dialog)) { dialogHandled = true; continue; } // 尝试找到并点击关闭按钮 const closeButtons = dialog.querySelectorAll('button, .close, .btn-close, [class*="close"], [class*="cancel"], a[href="#"]'); for (let button of closeButtons) { if (button.innerText.includes('关闭') || button.innerText.includes('取消') || button.innerText.includes('继续') || button.innerText.includes('确定') || button.className.includes('close')) { button.click(); console.log('自动关闭了弹窗'); dialogHandled = true; break; } } if (dialogHandled) break; } } if (dialogHandled) break; } // 特殊处理:超星学习通的弹题 if (!dialogHandled && this.siteName === '超星学习通') { // 超星特定的弹题处理 const topicMenus = document.querySelectorAll('.topic-menu, .ans-videoquiz'); if (topicMenus.length > 0) { // 尝试寻找继续按钮 const continueButtons = document.querySelectorAll('div[onclick*="continue"], a:contains("继续学习"), .ans-videoquiz-submit'); if (continueButtons.length > 0) { continueButtons[0].click(); dialogHandled = true; } // 如果没找到继续按钮,尝试自动选择答案 if (!dialogHandled) { const options = document.querySelectorAll('.ans-videoquiz-opt'); if (options.length > 0) { // 默认选第一个 options[0].click(); // 寻找提交按钮 setTimeout(() => { const submitBtn = document.querySelector('.ans-videoquiz-submit'); if (submitBtn) submitBtn.click(); }, 500); } } } } return dialogHandled; } // 尝试自动回答问题 tryToAnswerQuestion(dialog) { // 检查是否有选项 const options = dialog.querySelectorAll('input[type="radio"], input[type="checkbox"], .ans-videoquiz-opt'); if (options.length === 0) return false; // 智慧树和超星平台的问题处理 if (this.siteName === '智慧树' || this.siteName === '超星学习通') { // 随机选择一个选项(或第一个) const option = options[0]; option.click(); // 查找提交按钮 setTimeout(() => { const submitBtns = dialog.querySelectorAll('button[type="submit"], .submit-btn, .ans-videoquiz-submit, [class*="submit"]'); if (submitBtns.length > 0) { submitBtns[0].click(); return true; } }, 500); } return false; } // 禁用自动播放 disableAutoPlay() { if (this.mutationObserver) { this.mutationObserver.disconnect(); this.mutationObserver = null; } if (this.autoPlayInterval) { clearInterval(this.autoPlayInterval); this.autoPlayInterval = null; } // 更新自动播放状态 document.getElementById('autoplay-status').textContent = '已禁用'; document.getElementById('autoplay-status').style.color = '#888'; } // 启用自动下一章 enableAutoNext() { if (this.autoNextInterval) { clearInterval(this.autoNextInterval); } this.autoNextInterval = setInterval(() => { const videos = document.querySelectorAll('video'); videos.forEach(video => { if (video.ended) { this.findAndClickNextButton(); } }); }, 2000); } // 禁用自动下一章 disableAutoNext() { if (this.autoNextInterval) { clearInterval(this.autoNextInterval); this.autoNextInterval = null; } } // 寻找并点击下一章按钮 findAndClickNextButton() { // 针对不同平台查找下一章按钮 let nextBtn = null; // 超星学习通 if (this.siteName === '超星学习通') { nextBtn = document.querySelector('.ans-job-icon[title="下一章"]') || document.querySelector('.nextChapter'); } // 智慧树 else if (this.siteName === '智慧树') { nextBtn = document.querySelector('.next-page-btn') || document.querySelector('.next-btn'); } // 智慧职教 else if (this.siteName === '智慧职教') { nextBtn = document.querySelector('.next_lesson') || document.querySelector('.next-lesson'); } // 其他平台的通用选择器 else { const possibleSelectors = [ '.next', '.next-btn', '.next-lesson', '.nextChapter', '[title="下一章"]', '[title="下一节"]', '[title="下一讲"]', 'a:contains("下一章")', 'a:contains("下一节")' ]; for (let selector of possibleSelectors) { nextBtn = document.querySelector(selector); if (nextBtn) break; } } if (nextBtn) { nextBtn.click(); console.log('已自动跳转至下一章'); } } // 设置视频速度 applyVideoSpeed(speed) { const videos = document.querySelectorAll('video'); videos.forEach(video => { video.playbackRate = speed; }); // 持续应用速度(防止视频网站重置) if (this.speedInterval) { clearInterval(this.speedInterval); } this.speedInterval = setInterval(() => { const videos = document.querySelectorAll('video'); videos.forEach(video => { if (video.playbackRate !== speed) { video.playbackRate = speed; } }); // 更新速度进度条 const speedProgressBar = document.getElementById('speed-progress'); if (speedProgressBar) { const speedPercent = Math.min(100, (speed / 16) * 100); speedProgressBar.style.width = `${speedPercent}%`; } // 在面板上显示当前速度 const statusText = document.getElementById('video-status'); if (statusText && statusText.textContent.includes('播放中')) { statusText.textContent = `播放中 (${speed}x)`; } }, 1000); // 显示状态提示 this.showStatusTip(`视频速度已设置为 ${speed}x`, 2000); } // 启用拖动功能 enableDrag(elements) { elements.forEach(el => { el.addEventListener('mousedown', (e) => { const target = el.closest('#miaoke-helper-panel, #note-panel, #miaoke-helper-btn'); if (!target) return; // 初始位置 const initialX = e.clientX; const initialY = e.clientY; const startLeft = target.offsetLeft; const startTop = target.offsetTop; // 移动处理函数 const moveHandler = (e) => { const dx = e.clientX - initialX; const dy = e.clientY - initialY; target.style.left = startLeft + dx + 'px'; target.style.top = startTop + dy + 'px'; }; // 释放处理函数 const upHandler = () => { document.removeEventListener('mousemove', moveHandler); document.removeEventListener('mouseup', upHandler); }; document.addEventListener('mousemove', moveHandler); document.addEventListener('mouseup', upHandler); }); }); } // 更新视频状态 updateVideoStatus() { const videos = document.querySelectorAll('video'); if (videos.length > 0) { let anyPlaying = false; videos.forEach(video => { if (!video.paused && !video.ended) { anyPlaying = true; // 更新当前播放中的视频进度 this.updateVideoProgress(video); } }); const statusText = document.getElementById('video-status'); if (anyPlaying) { const currentSpeed = parseFloat(document.getElementById('play-speed').value); statusText.textContent = `播放中 (${currentSpeed}x)`; statusText.style.color = '#4CAF50'; } else if (videos[0].ended) { statusText.textContent = '已播放完毕'; statusText.style.color = '#FF9800'; } else { statusText.textContent = '已暂停'; statusText.style.color = '#F44336'; } } } // 更新视频进度条 updateVideoProgress(video) { if (!video) return; // 计算百分比 const percent = (video.currentTime / video.duration) * 100; const progressBar = document.getElementById('video-progress'); if (!isNaN(percent) && isFinite(percent)) { progressBar.style.width = `${percent}%`; // 更新速度进度条 const speedProgressBar = document.getElementById('speed-progress'); if (speedProgressBar) { const speedPercent = Math.min(100, (parseFloat(document.getElementById('play-speed').value) / 16) * 100); speedProgressBar.style.width = `${speedPercent}%`; } } } } // 页面加载完成后初始化 window.addEventListener('load', () => { // 延迟一点时间确保页面元素都已加载 setTimeout(() => { window.miaokeHelper = new MiaoKeHelper(); }, 1500); }); })();